﻿#include "CommonFunction.h"
#include <vtkWindowToImageFilter.h>
#include <vtkJPEGWriter.h>
#include <vtkImageResize.h>
#include <vtkSmartPointer.h>
#include <vtkActor.h>
#include <vtkMapper.h>
#include <vtkDataSet.h>
#include <vtkDataSetMapper.h>
#include <vtkProperty.h>
#include "gif.h"

#include <QDir>
#include <QFileInfo>
#include <QPixmap>
#include <QCoreApplication>
#include <QLayout>
#include <QImage>
#include <QFile>
#include <QDebug>
#include <QTextStream>
#include <QTableWidget>
#include <opencv2/core.hpp>
#include <opencv2/imgproc.hpp>
#include "BaseModule/IBaseSignal.h"


namespace pst
{

    // 字符串分割
    CommonFunction::CommonFunction()
    {

    }

    void CommonFunction::Stringsplit(const std::string& str, const std::string& splits,
        std::vector<std::string>& res)
    {
        if (str == "")
        {
            return;
        }
        //在字符串末尾也加入分隔符，方便截取最后一段
        std::string strs = str + splits;
        size_t pos = strs.find(splits);
        int step = splits.size();
        res.clear();
        // 若找不到内容则字符串搜索函数返回 npos
        while (pos != strs.npos)
        {
            std::string temp = strs.substr(0, pos);
            res.push_back(temp);
            //去掉已分割的字符串,在剩下的字符串中进行分割
            strs = strs.substr(pos + step, strs.size());
            pos = strs.find(splits);
        }
    }

    //去除字符串首尾空格
    std::string CommonFunction::trim(const std::string& s1)
    {
        std::string s = s1;
        if (!s.empty())
        {
            s.erase(0, s.find_first_not_of(" "));
            s.erase(s.find_last_not_of(" ") + 1);
        }
        return s;
    }

    void CommonFunction::SphereToRectangularCoorSys(const double theta, const double phi, const double r, double& x, double& y, double& z)
    {
        double my_pi = 3.141592653589793238462643383279502884L;
        double val = my_pi / 180;

        x = r * std::sin(theta * val) * std::cos(phi * val);
        y = r * std::sin(theta * val) * std::sin(phi * val);
        z = r * std::cos(theta * val);
    }

    void CommonFunction::PolarToRectangularCoorSys(const double theta, const double r, double& x, double& y)
    {
        double my_pi = 3.141592653589793238462643383279502884L;
        double val = my_pi / 180;
        x = r * std::cos(theta * val);
        y = r * std::sin(theta * val);
    }

    // Function to convert vtkImageData to cv::Mat  
    cv::Mat CommonFunction::VTKImageDataToCVMat(vtkImageData* vtkImage)
    {
        // Get the dimensions of the vtkImageData  
        int* dims = vtkImage->GetDimensions();

        // Determine the number of channels  
        int numChannels = vtkImage->GetNumberOfScalarComponents();

        // Create an appropriate OpenCV matrix  
        cv::Mat mat(dims[1], dims[0], CV_8UC3); // Assuming the data is RGB; you can adjust it according to the number of channels  

        // Get the pointer to the data  
        unsigned char* vtkData = static_cast<unsigned char*>(vtkImage->GetScalarPointer());

        // Copy data from VTK to OpenCV Mat  
        if (numChannels == 1)
        {
            // Grayscale image  
            for (int i = 0; i < dims[1]; ++i)
            {
                for (int j = 0; j < dims[0]; ++j)
                {
                    unsigned char value = vtkData[i * dims[0] + j];
                    mat.at<cv::Vec3b>(i, j) = cv::Vec3b(value, value, value); // Converting grayscale to RGB  
                }
            }
        }
        else if (numChannels == 3)
        {
            // RGB image  
            for (int i = 0; i < dims[1]; ++i)
            {
                for (int j = 0; j < dims[0]; ++j)
                {
                    unsigned char* pixel = vtkData + (i * dims[0] + j) * numChannels;
                    mat.at<cv::Vec3b>(dims[1] - i - 1, j) = cv::Vec3b(pixel[2], pixel[1], pixel[0]); // BGR format  
                }
            }
        }
        else
        {
            // Handle other cases if necessary  
            throw std::runtime_error("Unsupported number of channels");
        }

        return mat;
    }

    void CommonFunction::saveJPEGImageFromVTKScreen(vtkWindow* window, int width, int height, QString fileName, bool autoSize)
    {
        vtkNew<vtkWindowToImageFilter> windowToImageFilter;
        vtkNew<vtkImageResize> report_resize;
        vtkNew<vtkJPEGWriter> report_writer;

        windowToImageFilter->SetInput(window);
        report_resize->SetInputConnection(windowToImageFilter->GetOutputPort());
        if (autoSize)
        {
            windowToImageFilter->Update();
            auto dimension = windowToImageFilter->GetOutput()->GetDimensions();
            report_resize->SetOutputDimensions(dimension[0], dimension[1], -1);
        }
        else
        {
            report_resize->SetOutputDimensions(width, height, -1);
        }
        report_resize->Update();
        report_writer->SetFileName(fileName.toUtf8().constData());
        report_writer->SetInputConnection(report_resize->GetOutputPort());
        report_writer->Write();
    }

    // gif2rgb.c 文件中 RGB2GIF 只实现了 RGB to GIF 静态图功能
    // 这里做了修改，可以生成动态 GIF
    // 保存 GIF 的函数定义
    // 参数说明：
    //   frames          - 存储 cv::Mat 图像的列表（图像为 BGR 格式）
    //   outputFilename  - GIF 输出文件的路径（例如 "output.gif"）
    //   delay           - 每一帧延时，单位为 1/100 秒（例如 10 表示 100 毫秒/帧）
    bool CommonFunction::saveGifFromMatList(const QList<cv::Mat>& frames, const QString& outputFilename, int frameRate)
    {
        if (frames.isEmpty())
        {
            //qDebug() << __func__ << ": frames is empty！";
            return false;
        }

        auto animationTempIODir = createAnimationTempIODir("animationTempIO");

        for (size_t i = 0; i < frames.size(); ++i)
        {
            std::ostringstream oss;
            oss << animationTempIODir.toStdString() + "/frame_" << std::setw(4) << std::setfill('0') << i << ".png";
            cv::imwrite(oss.str(), frames[i]);
        }

        QStringList _pyCode;
        _pyCode.append(QString("PostProcessing.make_gif_from_frames('%1','%2',%3,'%4')").arg(animationTempIODir).arg(outputFilename).arg(frameRate).arg(".png"));
        emit IBaseSignalInstance->signal_execPYCode(_pyCode, true, true);

        deleteAnimationTempIODir(animationTempIODir);
    }

    vtkSmartPointer<vtkActor> CommonFunction::DeepCopyActorWithData(vtkActor* originalActor)
    {
        if (originalActor == nullptr)
        {
            return nullptr;
        }

        // 创建新的 vtkActor，并先通过 ShallowCopy 拷贝基本数据（几何变换、可见性等）
        vtkSmartPointer<vtkActor> copiedActor = vtkSmartPointer<vtkActor>::New();
        copiedActor->ShallowCopy(originalActor);

        // --------------------------------------------
        // 1. 深拷贝 vtkProperty
        // --------------------------------------------
        vtkProperty* originalProperty = originalActor->GetProperty();
        if (originalProperty)
        {
            vtkSmartPointer<vtkProperty> copiedProperty = vtkSmartPointer<vtkProperty>::New();
            copiedProperty->DeepCopy(originalProperty);
            copiedActor->SetProperty(copiedProperty);
        }

        //// --------------------------------------------
        //// 2. 深拷贝纹理 (如果需要)
        //// --------------------------------------------
        //vtkTexture* originalTexture = originalActor->GetTexture();
        //if (originalTexture)
        //{
        //    vtkSmartPointer<vtkTexture> copiedTexture = vtkSmartPointer<vtkTexture>::New();
        //    copiedTexture->DeepCopy(originalTexture);
        //    copiedActor->SetTexture(copiedTexture);
        //}

        // --------------------------------------------
        // 3. 深拷贝 mapper 及其数据
        // --------------------------------------------
        vtkMapper* originalMapper = originalActor->GetMapper();
        if (originalMapper)
        {
            // 针对 vtkPolyDataMapper 做特殊处理
            vtkDataSetMapper* originalPDM = vtkDataSetMapper::SafeDownCast(originalMapper);
            if (originalPDM)
            {
                vtkNew<vtkDataSetMapper> copiedMapper;

                // 如果 mapper 中包含输入数据，则拷贝数据对象
                vtkDataSet* originalInput = originalPDM->GetInput();
                if (originalInput)
                {
                    // 利用 NewInstance() 生成一个相同类型的对象，再调用 DeepCopy 进行数据拷贝
                    vtkSmartPointer<vtkDataObject> newInput = vtkSmartPointer<vtkDataObject>::Take(originalInput->NewInstance());
                    newInput->DeepCopy(originalInput);
                    // 注意这里通过 SafeDownCast 将新数据对象转为 vtkDataSet（一般 vtkPolyData、vtkUnstructuredGrid 等都继承自 vtkDataSet）
                    copiedMapper->SetInputData(vtkDataSet::SafeDownCast(newInput));
                }
                // 复制 mapper 其他属性（例如 scalar 映射、颜色范围等）采用 ShallowCopy 即可
                copiedMapper->ShallowCopy(originalPDM);

                copiedActor->SetMapper(copiedMapper);
            }
        }

        return copiedActor;
    }

    // 将 QPixmap 转换为 cv::Mat（BGR 格式）
    cv::Mat CommonFunction::QPixmapToCVMat(const QPixmap& pixmap)
    {
        // 1. 将 QPixmap 转换为 QImage
        QImage qImage = pixmap.toImage();
        // 2. 按需将 QImage 转换为 24 位 RGB 格式（如果原始图像有 alpha 通道，可选择不同的格式）
        QImage imageRGB = qImage.convertToFormat(QImage::Format_RGB888);
        // 3. 根据 QImage 生成 cv::Mat
        cv::Mat mat(imageRGB.height(), imageRGB.width(), CV_8UC3,
            const_cast<uchar*>(imageRGB.bits()), imageRGB.bytesPerLine());
        // 4. 注意：由于 OpenCV 常用 BGR，而这里转换后的 QImage 是 RGB，
        //    因此需要转换颜色顺序。如果你希望使用 RGB，可以省略这一步。
        cv::Mat matBGR;
        cv::cvtColor(mat, matBGR, cv::COLOR_RGB2BGR);
        return matBGR.clone();
    }



    void CommonFunction::exportTableWidgetToCSV(QTableWidget* tableWidget, const QString& filePath)
    {
        QFile file(filePath);
        // 打开文件，注意：QIODevice::Text 指定以文本模式写入
        if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
        {
            qWarning("无法打开文件进行写入");
            return;
        }

        QTextStream out(&file);
        out.setCodec("UTF-8");  // 设置编码，确保中文显示正常
        file.write("\xEF\xBB\xBF");

        int colCount = tableWidget->columnCount();
        int rowCount = tableWidget->rowCount();

        // 先写入表头 (横向表头)
        for (int col = 1; col < colCount; ++col)//第0列为markerID，不导出
        {
            QTableWidgetItem* headerItem = tableWidget->horizontalHeaderItem(col);
            QString headerText = headerItem ? headerItem->text() : "";
            // 如果文本中包含逗号，可加入引号避免分隔错误
            if (headerText.contains(","))
                headerText = "\"" + headerText + "\"";

            out << headerText;
            if (col < colCount - 1)
                out << ",";
        }
        out << "\n";

        // 写入数据行
        for (int row = 0; row < rowCount; ++row)
        {
            for (int col = 1; col < colCount; ++col)
            {
                QTableWidgetItem* item = tableWidget->item(row, col);
                QString cellText = item ? item->text() : "";
                if (cellText.contains(","))
                    cellText = "\"" + cellText + "\"";

                out << cellText;
                if (col < colCount - 1)
                    out << ",";
            }
            out << "\n";
        }

        file.close();
    }

    bool CommonFunction::extractContentBetweenParentheses(const QString& input, QString& output)
    {
        int leftIndex = input.indexOf('('); // 找到第一个左括号的索引
        int rightIndex = input.lastIndexOf(')'); // 找到最后一个右括号的索引

        if (leftIndex != -1 && rightIndex != -1 && leftIndex < rightIndex)
        {
            output = input.mid(leftIndex + 1, rightIndex - leftIndex - 1); // 提取括号之间的内容
            return true;
        }

        return false; // 如果没有匹配的括号或顺序不对，返回空字符串
    }

    void CommonFunction::clearLayout(QLayout* layout)
    {
        if (!layout)
            return;
        while (QLayoutItem* item = layout->takeAt(0))
        {
            // 如果是布局，递归清空它
            if (QLayout* childLayout = item->layout())
            {
                clearLayout(childLayout);
                delete childLayout;
            }
            // 如果是 widget，删除它
            if (QWidget* widget = item->widget())
            {
                widget->setParent(nullptr);
                delete widget;
            }
            delete item;
        }
    }


    /**
 * 创建 animationTempIO 目录
 * @return 成功时返回该目录的绝对路径，失败时返回空字符串
 */
    QString CommonFunction::createAnimationTempIODir(const QString& tempDirName)
    {
        // 1. 获取当前 exe 所在目录
        QString exeDir = QCoreApplication::applicationDirPath();
        QDir dir(exeDir);

        // 2. 构造要创建的子目录名和路径
        QString tempDirPath = dir.filePath(tempDirName);
        QDir tempDir(tempDirPath);

        // 3. 如果已存在旧目录，则先删除
#if QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)
        if (tempDir.exists())
        {
            if (!tempDir.removeRecursively())
            {
                qWarning() << "删除旧目录失败:" << tempDirPath;
                return QString();
            }
        }
#endif

        // 4. 创建新的 animationTempIO 目录
        if (!dir.mkdir(tempDirName))
        {
            qWarning() << "创建目录失败:" << tempDirPath;
            return QString();
        }

        qDebug() << "已创建目录:" << tempDirPath;
        return tempDirPath;
    }


    /**
    * 递归删除 animationTempIO 目录
    * @param path 要删除目录的绝对路径
    * @return 删除成功返回 true，否则 false
    */
    bool CommonFunction::deleteAnimationTempIODir(const QString& path)
    {
        QDir tempDir(path);
        if (!tempDir.exists())
        {
            qDebug() << "目录不存在，无需删除:" << path;
            return true;
        }

#if QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)
        if (!tempDir.removeRecursively())
        {
            qWarning() << "删除目录失败:" << path;
            return false;
        }
        qDebug() << "已删除目录:" << path;
        return true;
#else
        // Qt < 5.6 时需手动递归删除，可使用 QDirIterator 遍历子项并删除
        // 此处略
        return false;
#endif
    }


    bool CommonFunction::copyAndRenameFile(const QString& srcFilePath, const QString& dstFilePathWithName)
    {
        // 1. 校验源文件
        if (!QFile::exists(srcFilePath))
        {
            qWarning() << "源文件不存在：" << srcFilePath;
            return false;
        }

        // 2. 确保目标目录存在
        QFileInfo dstInfo(dstFilePathWithName);
        QDir dstDir = dstInfo.dir();
        if (!dstDir.exists() && !dstDir.mkpath("."))
        {
            qWarning() << "无法创建目标目录：" << dstDir.absolutePath();
            return false;
        }

        // 3. 如果目标文件已存在，先删除
        if (dstInfo.exists() && !QFile::remove(dstFilePathWithName))
        {
            qWarning() << "删除旧文件失败：" << dstFilePathWithName;
            return false;
        }

        // 4. 执行复制
        if (!QFile::copy(srcFilePath, dstFilePathWithName))
        {
            qWarning() << "复制文件失败：" << srcFilePath << "->" << dstFilePathWithName;
            return false;
        }

        return true;
    }

    // 与 vtkClipPolyData 默认一致：保留法向指向的一侧（insideOut=false）
    // insideOut=true 时，保留相反一侧。eps 用于数值容差。
    bool CommonFunction::isPointKeptByPlane(
        double px, double py, double pz,     // 点 p 的 xyz
        double ox, double oy, double oz,     // 平面 origin 的 xyz
        double nx, double ny, double nz,     // 平面 normal 的 xyz
        double eps )
    {
        const double dx = px - ox;
        const double dy = py - oy;
        const double dz = pz - oz;
        const double d = dx * nx + dy * ny + dz * nz; // 有向距离的比例（未归一化）

        // 针对六种法向分别判定
        if (nx > 0.0)
        {   // +X：平面与其正侧都保留
            const double d = px - ox;
            return d >= -eps;
        }
        else if (nx < 0.0)
        {   // -X：仅严格在 -X 侧保留（平面不保留）
            const double d = px - ox;
            return d < -eps;
        }
        else if (ny > 0.0)
        {   // +Y
            const double d = py - oy;
            return d >= -eps;
        }
        else if (ny < 0.0)
        {   // -Y
            const double d = py - oy;
            return d < -eps;
        }
        else if (nz > 0.0)
        {   // +Z
            const double d = pz - oz;
            return d >= -eps;
        }
        else
        {   // -Z
            const double d = pz - oz;
            return d < -eps;
        }
    }
}

